/* alloset.c is part of VIP program
 * Written by J. Salvador Arias, 2010
 *      CONICET, INSUE, Instituto Miguel Lillo,
 *      Miguel Lillo 205, S.M. de Tucuman (4000)
 *      Tucuman, Argentina
 *      e-mail: jsalarias@csnat.unt.edu.ar
 *
 * VIP is a free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation and
 * Creative Commons.
 *
 * This file and the VIP program is distributed in the hope
 * that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * To view a copy of this license, visit
 * http://creativecommons.org/licenses/GPL/2.0/
 * or send a letter to
 * Creative Commons, 171 Second Street, Suite 300,
 * San Francisco, California, 94105, USA.
 */

#include "biogeo.h"
#include "tessellation.h"

static SFLAG CompareSets (BITFIELD* nodeAsetA, BITFIELD* nodeAsetB, BITFIELD* nodeBsetA, BITFIELD* nodeBsetB);
static SFLAG IsNodeComp (PHYLONODE* nodeA, PHYLONODE* nodeB);

void DestroyAlloSet (ALLOPATRY* alloSet) {
 if (alloSet -> triangles != NULL)
   DestroyTriangleList (alloSet -> triangles);
 g_list_free (alloSet -> nodeList);
 g_slice_free1 (GETBITFIELDSIZE, alloSet -> setA);
 g_slice_free1 (GETBITFIELDSIZE, alloSet -> setB);
 g_slice_free1 (GETBITFIELDSIZE, alloSet -> baseA);
 g_slice_free1 (GETBITFIELDSIZE, alloSet -> baseB);
}

SFLAG IsNodeComp (PHYLONODE* nodeA, PHYLONODE* nodeB) {
 SFLAG toRet;
 SCOUNTER timeDiff;

 toRet = TRUE;
 /* Check if nodes are independent */
 if (nodeA -> tree == nodeB -> tree) {
   if (IsDesc (nodeA -> node, nodeB -> node))
     toRet = FALSE;
   else if (IsDesc (nodeB -> node, nodeA -> node))
     toRet = FALSE;
 }
 /* Check if nodes fit the time constraint */
 if (params . timeLag > 0) {
   timeDiff = (nodeA -> minAge >= nodeB -> minAge) ? nodeA -> minAge - nodeB -> minAge : nodeB -> minAge - nodeA -> minAge;
   if (timeDiff > params . timeLag)
     toRet = FALSE;
 }
 return toRet;
}

SFLAG CompareSets (BITFIELD* nodeAsetA, BITFIELD* nodeAsetB, BITFIELD* nodeBsetA, BITFIELD* nodeBsetB) {
 SFLAG toRet;

 toRet = FALSE;
 if (BitFieldAnd (nodeAsetA, nodeBsetA)) {
   if (BitFieldAnd (nodeAsetB, nodeBsetB) && (!(BitFieldAnd (nodeAsetA, nodeBsetB))) && (!(BitFieldAnd (nodeAsetB, nodeBsetA))))
     toRet = TRUE;
 }
 else if (BitFieldAnd (nodeAsetA, nodeBsetB)) {
   if (BitFieldAnd (nodeAsetB, nodeBsetA) && (!(BitFieldAnd (nodeAsetA, nodeBsetA))) && (!(BitFieldAnd (nodeAsetB, nodeBsetB))))
     toRet = TRUE;
 }
 return toRet;
}

void MakeCommonBarriers () {
 GList* navTree;
 GList* nuList;
 GList* navComp;
 GNode* navNode;
 PHYLOTREE* tree;
 PHYLONODE* node;
 PHYLONODE* compNode;
 SCOUNTER numNodes, i, j, k;
 GPtrArray* nodeArray;
 BITFIELD* tmpField;
 RECONS* actRec;

 /* Checks */
 tree = GETTREES -> data;
 if (tree -> recCons != NULL) {
   g_string_append (outLog, "\n\nConsensus of reconstructions");
   MakeConsensus ();
   return;
 }
 RemoveAlloSets ();
 MakeConsensus ();

 /* Initialization */
 numNodes = 0;
 tmpField = CreateBitField ();
 for (navTree = GETTREES; navTree != NULL; navTree = g_list_next (navTree)) {
   tree = (PHYLOTREE*) navTree -> data;
   node = (PHYLONODE*) tree -> root -> data;
   numNodes += node -> rec -> numVics;
 }
 nodeArray = g_ptr_array_sized_new (numNodes);
 for (navTree = GETTREES; navTree != NULL; navTree = g_list_next (navTree)) {
   tree = (PHYLOTREE*) navTree -> data;
   for (j = 0; j < tree -> nodeArray -> len; ++ j) {
     navNode = (GNode*) g_ptr_array_index (tree -> nodeArray, j);
     node = (PHYLONODE*) navNode -> data;
     if (node -> rec -> nodeFlag & VIC_NODE) {
       g_ptr_array_add (nodeArray, node);
       node -> alloSet = g_slice_alloc (sizeof (ALLOPATRY));
       node -> alloSet -> setA = CreateBitField ();
       node -> alloSet -> setB = CreateBitField ();
       CopyBitField (node -> alloSet -> setA, node -> rec -> setA -> rec -> cells);
       CopyBitField (node -> alloSet -> setB, node -> rec -> setB -> rec -> cells);
       BitFieldRemove (node -> alloSet -> setA, node -> rec -> setB -> rec -> cells);
       BitFieldRemove (node -> alloSet -> setB, node -> rec -> setA -> rec -> cells);
       node -> alloSet -> baseA = CreateBitField ();
       node -> alloSet -> baseB = CreateBitField ();
       CopyBitField (node -> alloSet -> baseA, node -> alloSet -> setA);
       CopyBitField (node -> alloSet -> baseB, node -> alloSet -> setB);
       node -> alloSet -> nodeList = NULL;
       node -> alloSet -> numNodes = 0;
       node -> alloSet -> triangles = NULL;
     }
   }
 }

 /* Compare nodes */
 for (j = 0; j < (nodeArray -> len - 1); ++ j) {
   node = (PHYLONODE*) g_ptr_array_index (nodeArray, j);
   for (k = j + 1; k < nodeArray -> len; ++ k) {
     compNode = (PHYLONODE*) g_ptr_array_index (nodeArray, k);
     if (IsNodeComp (node, compNode) == FALSE) continue;
     if (CompareSets (node -> alloSet -> setA, node -> alloSet -> setB, compNode -> alloSet -> setA, compNode -> alloSet -> setB)
                                     == FALSE) continue;
     nuList = InsertListElement (node -> alloSet -> nodeList, compNode);
     if (node -> alloSet -> nodeList == NULL)
       node -> alloSet -> nodeList = nuList;
     ++ node -> alloSet -> numNodes;
     nuList = InsertListElement (compNode -> alloSet -> nodeList, node);
     if (compNode -> alloSet -> nodeList == NULL)
       compNode -> alloSet -> nodeList = nuList;
     ++ compNode -> alloSet -> numNodes;
   }
 }
 /* Add distros */

 for (j = 0; j < nodeArray -> len; ++ j) {
   node = (PHYLONODE*) g_ptr_array_index (nodeArray, j);
   if (node -> alloSet -> numNodes == 0) continue;
   tree = node -> tree;
   ++ tree -> suppNodes;
   for (navComp = node -> alloSet -> nodeList; navComp != NULL; navComp = g_list_next (navComp)) {
     compNode = (PHYLONODE*) navComp -> data;
     if (BitFieldAnd (node -> alloSet -> baseA, compNode -> alloSet -> baseA)) {
       BitFieldORsum (node -> alloSet -> setA, compNode -> alloSet -> baseA);
       BitFieldORsum (node -> alloSet -> setB, compNode -> alloSet -> baseB);
     }
     else {
       BitFieldORsum (node -> alloSet -> setA, compNode -> alloSet -> baseB);
       BitFieldORsum (node -> alloSet -> setB, compNode -> alloSet -> baseA);
     }
   }
   CopyBitField (tmpField, node -> alloSet -> setA);
   BitFieldRemove (node -> alloSet -> setA, node -> alloSet -> setB);
   BitFieldRemove (node -> alloSet -> setB, tmpField);
 }
 g_slice_free1 (GETBITFIELDSIZE, tmpField);
 g_ptr_array_free (nodeArray, TRUE);

 /* Write the output */
 i = 0;
 for (navTree = GETTREES; navTree != NULL; navTree = g_list_next (navTree)) {
   tree = (PHYLOTREE*) navTree -> data;
   actRec = (RECONS*) g_ptr_array_index (tree -> recCons, 0);
   g_string_append_printf (outLog, "\n\nTree %ld, (%s)\nDisjunt sister pairs: %ld\nSupported disjunctions: %ld\n",
                           i, tree -> name, actRec -> numVics, tree -> suppNodes);
   ++ i;
 }
}
